package fun.yao.common.nio;

import fun.yao.common.Constant;
import fun.yao.common.monitor.MonitorThread;
import fun.yao.common.security.SSLConstant;
import fun.yao.common.uitls.LogUtil;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * 使用Java.nio进行异步通信的线程基类
 */
public abstract class AbstractNetThread extends Thread
{

    /** 选择器 */
    protected Selector mSelector = null ;

    /** 是否运行 */
    protected boolean bIsRun = true;

    /**接收到新Socket的回调方法*/
    protected Method callAcceptMethod = null;
    /**接收到新Socket的回调对象*/
    protected Object callAcceptObj = null;

    /**SocketChannel连接建立或断开的回调方法*/
    protected Method callConnectMethod = null;
    /**SocketChannel有可读取数据时的回调对象*/
    protected Object callConnectObj = null;

    /**SocketChannel有可读取数据时的回调方法*/
    protected Method callReadMethod = null;
    /**SocketChannel有可读取数据时的回调对象*/
    protected Object callReadObj = null;

    /**SocketChannel有可写数据时的回调方法*/
    protected Method callWriteMethod = null;
    /**SocketChannel有可写数据时的回调对象*/
    protected Object callWriteObj = null;

    /**读取到完整报文的回调方法*/
    protected Method callReadFullMsgMethod = null;
    /**读取到完整报文的回调对象*/
    protected Object callReadFullMsgObj = null;

    //---------------------------------------------------------- 加密加关 add by csl 2008.11.11
    /** 加密模式，默认不加密 */
    protected byte mEncryptMode = SSLConstant.ENCRYPT_MODE_NO ;

    /** 会话ID */
    protected String mSessionId = "";

    /**构造函数*/
    public AbstractNetThread()
    {
    }

    /**
     * 设置加密信息
     * @param iEncryptMode  加密模式
     * @param sSessionId    会话ID
     */
    public void setEncryptInfo(byte iEncryptMode,String sSessionId)
    {
        this.mEncryptMode = iEncryptMode;
        this.mSessionId   = sSessionId;
    }

    //-------------------------------------------------------------------------  注册回调方法
    /**
     * 设置接收到一个新Socket的回调方法。回调方法应尽可能的快速处理
     * 回调方法原形为：method(Selector selector,SocketChannel sc);
     * @param objCall      接收Socket的实例
     * @param sMethodName  接收的类的方法名称
     */
    public void regAcceptMethod(Object objCall,String sMethodName)
            throws NoSuchMethodException,SecurityException
    {
        this.callAcceptMethod = objCall.getClass().getMethod(sMethodName,new Class[]{Selector.class,SocketChannel.class});
        this.callAcceptObj = objCall;
    }

    /**
     * 设置接收到Socket建立或断开的回调方法。回调方法应尽可能的快速处理
     * 回调方法原形为：method(Selector selector,SocketChannel sc);
     * @param objCall      接收Socket的实例
     * @param sMethodName  接收的类的方法名称
     */
    public void regConnectMethod(Object objCall,String sMethodName)
            throws NoSuchMethodException,SecurityException
    {
        this.callConnectMethod = objCall.getClass().getMethod(sMethodName,new Class[]{Selector.class,SocketChannel.class});
        this.callConnectObj = objCall;
    }

    /**
     * 设置SocketChannel有可读取数据时回调方法
     * 回调方法原形为：method(Selector selector,SocketChannel sc);
     * @param objCall     读取SocketChannel数据的实例
     * @param sMethodName 读取SocketChannel数据的方法
     */
    public void regReadMethod(Object objCall,String sMethodName)
            throws NoSuchMethodException,SecurityException
    {
        this.callReadMethod =  objCall.getClass().getMethod(sMethodName,new Class[]{Selector.class,SocketChannel.class});
        this.callReadObj = objCall;
    }

    /**
     * 设置SocketChannel有可写取数据时回调方法
     * 回调方法原形为：method(Selector selector,SocketChannel sc);
     * @param objCall     写入SocketChannel数据的实例
     * @param sMethodName 写入SocketChannel数据的方法
     */
    public void regWriteMethod(Object objCall,String sMethodName)
            throws NoSuchMethodException,SecurityException
    {
        this.callWriteMethod =  objCall.getClass().getMethod(sMethodName,new Class[]{Selector.class,SocketChannel.class});
        this.callWriteObj = objCall;
    }

    /**
     * 设置读取到完整报文时回调方法
     * 回调方法原形为：method(SocketChannel vSocketChannel, ByteBuffer sFullMsgBuff);
     * @param objCall     写入SocketChannel数据的实例
     * @param sMethodName 写入SocketChannel数据的方法
     */
    public void regReadFullMsgMethod(Object objCall,String sMethodName)
            throws NoSuchMethodException,SecurityException
    {
        this.callReadFullMsgMethod =  objCall.getClass().getMethod(sMethodName,new Class[]{SocketChannel.class,ByteBuffer.class});
        this.callReadFullMsgObj = objCall;
    }

    /**初始化*/
    protected abstract void init() throws Exception ;

    /**关闭处理*/
    protected abstract void close();

    /** 线程空闲时调用 */
    protected void idlesse()
    {

    }

    /**
     * 线程方法
     */
    public void run()
    {
        int iIdleNum = 0;

        try{
            //初始化
            init();

            String sName = "AbstractNetThread-" + this.getName();

            //循环接收事件并处理
            while ( bIsRun)
            {
                try
                {
                    if ( mSelector != null && mSelector.isOpen() )
                    {
                        int num = mSelector.select(2000);
                        try
                        {
                            MonitorThread.addCycleInvoke(sName);
                            if ( num > 0 )
                            {
                                Iterator<SelectionKey> it = mSelector.selectedKeys().iterator();

                                while (it.hasNext())
                                {
                                    SelectionKey s = (SelectionKey)it.next();
                                    it.remove();
                                    doEvent(s);
                                }

                                iIdleNum = 0;

                            }else
                            {
                                if ( ++iIdleNum % 5 == 0 )
                                {
                                    iIdleNum =  0;
                                    idlesse();
                                }
                            }
                        }catch(Exception ee)
                        {
                            LogUtil.WriteLog(Constant.BADLY_ERROR,ee);
                        }
                    }else{
                        Thread.sleep(10);
                    }
                }catch(Exception ee)
                {
                    LogUtil.WriteLog(Constant.BADLY_ERROR,ee);
                }
            }
        }catch(Exception e)
        {
            LogUtil.WriteLog(Constant.BADLY_ERROR,e);
        }finally{
            //关闭处理
            close();
        }
        LogUtil.writeSystemLog(Constant.NOTICE, "连接服务终止.");
    }

    /**处理事件*/
    private void doEvent(SelectionKey s)
    {
        try
        {

            // 读取事件
            if (s.isValid() && s.isReadable() && this.callReadMethod != null && this.callReadObj != null )
            {
                SocketChannel sc = (SocketChannel) s.channel();
                this.callReadMethod.invoke(this.callReadObj,new Object[]{this.mSelector,sc});
            }

            // 写入事件
            if (s.isValid() && s.isWritable() && this.callWriteMethod != null && this.callWriteObj != null )
            {
                SocketChannel sc = (SocketChannel) s.channel();
                this.callWriteMethod.invoke(this.callWriteObj,new Object[]{this.mSelector,sc});
            }

            //连接事件
            if (s.isValid() && s.isAcceptable() && this.callAcceptMethod != null && this.callAcceptObj != null )
            {
                SocketChannel sc = ((ServerSocketChannel) s.channel()).accept();
                //sc.configureBlocking(false);
                this.callAcceptMethod.invoke(this.callAcceptObj,new Object[]{this.mSelector,sc});
            }

            //连接建立或者出错事件
            if (s.isValid() && s.isConnectable() && this.callConnectMethod != null && this.callConnectObj != null )
            {
                SocketChannel sc = (SocketChannel) s.channel();
                this.callConnectMethod.invoke(this.callConnectObj,new Object[]{this.mSelector,sc});
            }

        }catch(Exception e)
        {
            LogUtil.WriteLog(Constant.BADLY_ERROR,e);
        }
    }

}
